///////////////////////////////////////////////////////////////////////////////
////  (c) Chaichuk AU (chaichuk@mail.ru), 								   ////
////  2004, Chelyabinsk, NTC Privodnaya Tehnika (momentum@chel.surnet.ru)  ////
///////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <time.h>

#include "../api.h"
#include "ct_ansi_io.h"


#define EOT 0x04
#define ENQ 0x05
#define STX 0x02
#define ETX 0x03
#define ACK 0x06
#define BS  0x08
#define NAK 0x15


//#define DEBUG_CTANSI_IO

//-----------------------------------------------------------------------------
// получить текущее время в виде строки (для сообщений об ошибках)
//-----------------------------------------------------------------------------
char* get_time_now ()
{
	time_t tod;
	static char out [100];
	
	time (&tod);
    strftime (out, sizeof (out), "%d-%m-%Y %T", localtime (&tod));	
    
    return out;
}

//-----------------------------------------------------------------------------
// открыть порт
//-----------------------------------------------------------------------------
int open_port (const char *port_name, int speed)
{
	int fd;
	struct termios termios_p;
	
	fd = open (port_name, O_RDWR);
	if (fd == -1) {
		perror ("Error opening port. ");
		exit (-1);	
	}
	tcgetattr (fd, &termios_p);
	cfmakeraw (&termios_p);
	termios_p.c_cflag = CS7 | PARENB | CREAD;
	termios_p.c_iflag = INPCK;
	termios_p.c_lflag = IEXTEN;
	termios_p.c_oflag = 0;
	termios_p.c_ispeed = speed;
	termios_p.c_ospeed = speed;
	termios_p.c_cc [VFWD] = ETX;
	tcsetattr (fd, TCSANOW, &termios_p);
	
	return fd;
}

//-----------------------------------------------------------------------------
// расчет контрольной суммы
//-----------------------------------------------------------------------------
uint8_t calc_bcc (const char * msg, int size)
{
	uint8_t bcc = 0;
	int i;
	
	for (i=0; i<size; i++) 
		bcc ^= msg[i];	

	if (bcc < 32) bcc += 32;
	return bcc;
}

//-----------------------------------------------------------------------------
// чтение данных по протоколу CT ANSI
//-----------------------------------------------------------------------------
int ctansi_read (int port_fd, int node_id, int addr, double *buf)
{
	char  out_buf[255], in_buf[255];
	int ret;
	uint8_t bcc_ret;
	int addr_ret;
	int start_byte;
	int i;
	
	int node_id_1  = node_id - (int)((node_id)/10)*10;
	int node_id_10 = (int)((node_id)/10);
	
	sprintf (out_buf, "%c%1d%1d%1d%1d%04d%c", EOT, node_id_10, node_id_10, node_id_1, node_id_1, addr, ENQ);
	
	if (write (port_fd, out_buf, strlen (out_buf)) == -1) {
		perror ("Error writing to port. ");
		return (-1);
	}

	ret = readcond (port_fd, in_buf, 20, 20, 1, 1);
	if (ret == -1) {
		perror ("Error while waiting for ETX. ");
		return (-1);
	}	
	
	#ifdef DEBUG_CTANSI_IO
	printf ("received: ");
	for (i= 0; i< ret; i++) {
		printf (" 0x%02x");
	}
	printf ("\n");
	#endif
	
	for (start_byte = 0; start_byte < ret; start_byte ++) {
		if (in_buf [start_byte] == STX) {
			break;
		}		
	}
	
	if (start_byte == ret) {	
		for (i= 0; i< ret; i++) {
			if (in_buf [i] == EOT) {
				fprintf (stderr, "%s: Wrong address (node_id = %02d, addr = %04d, EOT received\n", get_time_now (), node_id, addr);
				return (0);
			}
		}
		fprintf (stderr, "%s: Wrong packet without STX\n", get_time_now ());
		return (-1);
	}

	in_buf [ret] = '\0';
	if (readcond (port_fd, &bcc_ret, 1, 1, 1, 1) == -1) {
		perror ("Error while waiting for BCC. ");
		return (-1);
	}
	if (bcc_ret != calc_bcc (&in_buf [start_byte + 1], ret-1)) {
		fprintf (stderr, "%s: Wrong BCC\n", get_time_now ());
		return (-1);
	}
	in_buf [ret-1] = '\0';
	sscanf (&in_buf [start_byte + 1], "%d", &addr_ret);
	sscanf (&in_buf [start_byte + 5], "%lf", buf);

	return 0;
}